﻿using CTM.Web.Models.Input;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Security.Claims;
using System.Threading.Tasks;

namespace CTM.Web.Models
{
    // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
    public class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }

        public string Avatar { get; set; }

        [StringLength(100)]
        public string DisplayName { get; set; }

        [StringLength(100)]
        public string NTID { get; set; }

        /// <summary>
        /// "enum" from MIS db
        /// </summary>
        [StringLength(100)]
        public string EmpNo { get; set; }

        #region Relationship

        [InverseProperty("Manager")]
        public virtual ICollection<Org> ManageOrgs { get; set; }

        //public virtual ICollection<Org> Orgs { get; set; }
        public virtual Org Org { get; set; }

        public virtual ApplicationUser Manager { get; set; }
        public virtual ICollection<ApplicationUser> Employees { get; set; }

        [InverseProperty("Manager")]
        public virtual ICollection<Post> ManagePosts { get; set; }

        [InverseProperty("Employee")]
        public virtual ICollection<Post> Posts { get; set; }

        [InverseProperty("Receiver")]
        public virtual ICollection<Notification> ComingNotifications { get; set; }

        [InverseProperty("Giver")]
        public virtual ICollection<Notification> SendingNotifications { get; set; }

        public DateTime? TransferredOn { get; set; }

        #endregion

        public ApplicationUser()
            : base()
        {
            ManageOrgs = new HashSet<Org>();
            //Orgs = new HashSet<Org>();
            Employees = new HashSet<ApplicationUser>();
        }
    }

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection", throwIfV1Schema: false)
        {
            //Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, CTM.Web.Migrations.Configuration>("DefaultConnection"));            
        }

        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }

        #region Properties
        public new DbSet<ApplicationRole> Roles { get; set; }
        public DbSet<Permission> Permissions { get; set; }
        public DbSet<Post> Posts { get; set; }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Comment> Comments { get; set; }
        public DbSet<Tag> Tags { get; set; }
        public DbSet<Org> Orgs { get; set; }
        public DbSet<OrgType> OrgTypes { get; set; }
        public DbSet<Notification> Notifications { get; set; }
        #endregion

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<ApplicationRole>()
                   .HasMany<Permission>(r => r.Permissions)
                   .WithMany(p => p.Roles)
                   .Map(rp =>
                   {
                       rp.MapLeftKey("RoleId");
                       rp.MapRightKey("PermissionId");
                       rp.ToTable("RolePermissions");
                   });

            modelBuilder.Entity<Post>()
                .HasRequired(p => p.Category)
                .WithMany(c => c.Posts)
                .Map(m =>
                {
                    m.MapKey("CategoryId");
                }).WillCascadeOnDelete(false);

            modelBuilder.Entity<Comment>()
                .HasRequired(c => c.Post)
                .WithMany(c => c.Comments)
                .Map(m =>
                {
                    m.MapKey("PostId");
                }).WillCascadeOnDelete(false);

            //modelBuilder.Entity<Tag>()
            //   .HasRequired(c => c.Post)
            //   .WithMany(c => c.Tags)
            //   .Map(m =>
            //   {
            //       m.MapKey("PostId");
            //   });
            modelBuilder.Entity<Post>()
                .HasMany<Tag>(p => p.Tags)
                .WithMany(t => t.Posts)
                .Map(rp =>
                {
                    rp.MapLeftKey("PostId");
                    rp.MapRightKey("TagId");
                    rp.ToTable("PostTags");
                });

            modelBuilder.Entity<Org>()
                .HasRequired(o => o.Type)
                .WithMany(c => c.Orgs)
                .Map(m =>
                {
                    m.MapKey("OrgTypeId");
                }).WillCascadeOnDelete(false);

            modelBuilder.Entity<Org>()
                //.HasRequired(o => o.Manager)
                .HasOptional(o => o.Manager)
                .WithMany(u => u.ManageOrgs)
                .Map(m =>
                {
                    m.MapKey("ManagerId");
                }).WillCascadeOnDelete(false);

            modelBuilder.Entity<Org>()
                .HasOptional(o => o.Parent)
                .WithMany(o => o.Children)
                .HasForeignKey(o => o.ParentId);

            //modelBuilder.Entity<ApplicationUser>()
            //       .HasMany<Org>(u => u.Orgs)
            //       .WithMany(o => o.Employees)
            //       .Map(rp =>
            //       {
            //           rp.MapLeftKey("UserId");
            //           rp.MapRightKey("OrgId");
            //           rp.ToTable("UserOrgs");
            //       });
            modelBuilder.Entity<ApplicationUser>()
                //.HasRequired(u => u.Org)
                .HasOptional(u => u.Org)
                .WithMany(o => o.Employees)
                .Map(m =>
                {
                    m.MapKey("OrgId");
                }).WillCascadeOnDelete(false);

            modelBuilder.Entity<ApplicationUser>()
                .HasOptional(u => u.Manager)
                .WithMany(o => o.Employees)
                .Map(m =>
                {
                    m.MapKey("ManagerId");
                }).WillCascadeOnDelete(false);

            modelBuilder.Entity<Post>()
                .HasRequired(c => c.Manager)
                .WithMany(u => u.ManagePosts)
                .Map(m =>
                {
                    m.MapKey("ManagerId");
                }).WillCascadeOnDelete(false);

            modelBuilder.Entity<Post>()
                .HasRequired(c => c.Employee)
                .WithMany(u => u.Posts)
                .Map(m =>
                {
                    m.MapKey("EmployeeId");
                }).WillCascadeOnDelete(false);

            modelBuilder.Entity<Notification>()
                .HasOptional(n => n.Post)
                .WithMany(p => p.Notifications)
                .Map(m =>
                {
                    m.MapKey("PostId");
                }).WillCascadeOnDelete(false);

            modelBuilder.Entity<Notification>()
                .HasOptional(n => n.Comment)
                .WithMany(c => c.Notifications)
                .Map(m =>
                {
                    m.MapKey("CommentId");
                }).WillCascadeOnDelete(false);

            modelBuilder.Entity<Notification>()
                .HasOptional(n => n.Giver)
                .WithMany(c => c.SendingNotifications)
                .Map(m =>
                {
                    m.MapKey("GiverId");
                }).WillCascadeOnDelete(false);

            modelBuilder.Entity<Notification>()
                .HasOptional(n => n.Receiver)
                .WithMany(c => c.ComingNotifications)
                .Map(m =>
                {
                    m.MapKey("ReceiverId");
                }).WillCascadeOnDelete(false);
        }
    }
}